--添加新的小丑牌
--模组环境运行 修改cardlist的内容
--需要动画 动画名字anim和通道的名字folername
local BALATRO_UTIL = require("prefabs/balatro_util")
local BALATRO_SCORE_UTILS = require("prefabs/balatro_score_utils")
local Image = require("widgets/image")
local BalatroWidget = require "widgets/redux/balatrowidget"
local JOKER_SCALE = 0.7
local cardlist = {
    qingque = {
        name = "qingque",
        desc = "获得牌点数大于弃置牌时+10倍率\n获得牌点数小于弃置-10倍率\n若技能生效6次则+9999倍率",
        anim = "jocker_qingque",
        folername = "jocker_qingque",
        configJockerClass = {
            OnNewCards = function(self, oldcards)
                for i = 1, BALATRO_UTIL.NUM_SELECTED_CARDS do
                    if oldcards[i] then
                        local old = self:GetCardNumberByIndex(i, oldcards)
                        old = old == 1 and 11 or old
                        local now = self:GetCardNumberByIndex(i)
                        now = now == 1 and 11 or now
                        if now > old then
                            self:AddMult(10)
                        elseif now < old then
                            self:AddMult(-10)
                        else
                        end
                    end
                end
            end,
        },
        configJockerWidget = {
            Score_pst = function(self, card, oldcard)
                local oldsuit = math.floor(oldcard / 100)
                local newsuit = math.floor(self.slots[card] / 100)
                local newnum = self.slots[card] - (newsuit * 100)
                newnum = newnum == 1 and 11 or newnum
                local oldnum = oldcard - (oldsuit * 100)
                oldnum = oldnum == 1 and 11 or oldnum
                if self.discard[card] then
                    if oldnum ~= newnum then
                        table.insert(self.queue, {
                            time = 15 * FRAMES,
                            fn = function()
                                self.root.joker_card:SetScale(JOKER_SCALE * 1.3)
                                self.root.joker_card:SetRotation(15)
                                self.root.machine["card" .. card]:SetRotation(15)
                                TheFrontEnd:GetSound():PlaySound("balatro/balatro_cabinet/cards_flip_HUD")
                            end
                        })
                        if oldnum ~= newnum then
                            for s = 1, 10 do
                                table.insert(self.queue, {
                                    time = 0.5 * FRAMES,
                                    fn = function()
                                        TheFrontEnd:GetSound():PlaySound("balatro/balatro_cabinet/mult_HUD", "mult")
                                        TheFrontEnd:GetSound():SetParameter("mult", "mult_amt", self:calcMultSnd(s))
                                        self:AddMult(newnum > oldnum and 1 or -1)
                                    end
                                })
                            end
                        end

                        table.insert(self.queue, { time = 1 * FRAMES })

                        table.insert(self.queue, {
                            time = 0 * FRAMES,
                            fn = function()
                                self.root.joker_card:SetScale(JOKER_SCALE * 1)
                                self.root.joker_card:SetRotation(12)
                                self.root.machine["card" .. card]:SetRotation(0)
                                TheFrontEnd:GetSound():PlaySound("balatro/balatro_cabinet/cards_flip_HUD")
                            end
                        })
                    end
                end
            end
        },
    }
}
--获取local函数
local function GetUpvalueHelper(entry_fn, entry_name)
    local i = 1
    while true do
        local name, value = debug.getupvalue(entry_fn, i)
        if name then
            print(name, value)
        end
        if name == entry_name then
            return value, i
        elseif name == nil then
            return
        end
        i = i + 1
    end
end
local function GetUpvalueHelper_deekseek(entry_fn, entry_name, fn, path, depth)
    local i = 1
    local tab = {}
    while true do
        local name, value = debug.getupvalue(entry_fn, i)
        if name then
            print(name, value)
        end
        if name == entry_name then
            return value, i
        elseif name == nil then
            break
        elseif type(value) == "function" then
            tab[value] = true
        end
        i = i + 1
    end
    for value, v in pairs(tab) do
        local fn1, ii, prv = MOD_util:GetUpvalue_deekseek(value, path, depth + 1)
        if fn1 then
            return fn1, ii, prv
        end
    end
end
local MOD_util = {}

function MOD_util:GetUpvalue(fn, path) --fn是要总函数，path是要改的本地函数
    local prv, i = nil, nil
    for var in path:gmatch("[^%.]+") do
        prv = fn
        fn, i = GetUpvalueHelper(fn, var)
        if not fn then break end
    end
    return fn, i, prv --返回那个本地函数和所在行
end

function MOD_util:SetUpvalue(start_fn, path, new_fn) --start_fn是要总函数，path是要改的本地函数，new_fn是修改后的
    local fn, fn_i, scope_fn = self:GetUpvalue(start_fn, path)
    if not fn_i then
        print("Didn't find " .. path .. " from", start_fn)
        return
    end
    debug.setupvalue(scope_fn, fn_i, new_fn)
end

function MOD_util:GetUpvalue_deekseek(fn, path, depth) --fn是要总函数，path是要改的本地函数
    depth = depth or 0                                 --最多四层
    if depth >= 4 then return end
    local prv, i = nil, nil
    for var in path:gmatch("[^%.]+") do
        prv         = fn
        fn, i, prv2 = GetUpvalueHelper_deekseek(fn, var, fn, path, depth)
        if not fn then break end
    end
    return fn, i, prv2 or prv --返回那个本地函数和所在行
end

function MOD_util:SetUpvalue_deekseek(start_fn, path, new_fn) --start_fn是要总函数，path是要改的本地函数，new_fn是修改后的
    local fn, fn_i, scope_fn = self:GetUpvalue_deekseek(start_fn, path, 0)
    if not fn_i then
        print("Didn't find " .. path .. " from", start_fn)
        return
    end
    debug.setupvalue(scope_fn, fn_i, new_fn)
end

local JOKERS = BALATRO_SCORE_UTILS.JOKERS
local BaseJoker = JOKERS.walter._base
local jokers = MOD_util:GetUpvalue_deekseek(BalatroWidget.StartSelectJoker, "jokers")
for k, v in pairs(cardlist) do
    local charname = v.name
    table.insert(BALATRO_UTIL.AVAILABLE_JOKERS, charname)
    BALATRO_UTIL.AVAILABLE_JOKER_IDS[charname] = #BALATRO_UTIL.AVAILABLE_JOKERS
    local ModJoker = Class(BaseJoker, function(self, cards)
        BaseJoker._ctor(self, cards)
    end)
    for kk, vv in pairs(v.configJockerClass) do
        ModJoker[kk] = vv
    end
    JOKERS[charname] = ModJoker
    STRINGS.BALATRO["JOKER_" .. v.name] = v.desc
    if v.atlas then
        table.insert(Assets, Asset("ATLAS", v.atlas))
        table.insert(Assets, Asset("ATLAS_BUILD", v.atlas, 256))
    end
    if v.anim then
        table.insert(Assets, Asset("ANIM", string.format("anim/%s.zip", v.anim)))
    end
    jokers[charname] = { name = charname, desc = STRINGS.BALATRO["JOKER_" .. v.name] }
end
local hookingwebber = false
local hookjocker = nil
local oldReceiveDeal = BalatroWidget.ReceiveDeal
function BalatroWidget:ReceiveDeal(...)
    if cardlist[self.joker] then
        local v = cardlist[self.joker]
        if v.configJockerWidget then
            if v.configJockerWidget then
                if v.configJockerWidget.ScoreAll then
                    v.configJockerWidget.ScoreAll(self)
                end
                if v.configJockerWidget.Score then
                    for i = 1, BALATRO_UTIL.NUM_SELECTED_CARDS do
                        v.configJockerWidget.Score(self, i)
                    end
                end
            end
        end
        local old = self.joker
        hookjocker = old
        self.joker = "webber"
        hookingwebber = true
        oldReceiveDeal(self, ...)
        hookingwebber = false
        self.joker = old
        return
    end

    return oldReceiveDeal(self, ...)
end

--hook the webber!
local oldScoreWebber = BalatroWidget.ScoreWebber
function BalatroWidget:ScoreWebber(card, oldcard)
    if hookingwebber then
        local v = hookjocker and cardlist[hookjocker]
        if v and v.configJockerWidget then
            if v.configJockerWidget then
                if v.configJockerWidget.Score_pst then
                    v.configJockerWidget.Score_pst(self, card, oldcard)
                end
            end
        end
        return
    end
    return oldScoreWebber(self, card, oldcard)
end

local oldStartSelectJoker = BalatroWidget.StartSelectJoker
function BalatroWidget:StartSelectJoker(...)
    oldStartSelectJoker(self, ...)
    for i = 1, BALATRO_UTIL.NUM_JOKER_CHOICES do
        local card = self.root["joker_card" .. i]
        local v = cardlist[BALATRO_UTIL.AVAILABLE_JOKERS[card.joker_id]]
        if v then
            if v.anim then
                card.uianim:GetAnimState():OverrideSymbol("swap_card1", v.anim,
                    v.folername or ("joker_" .. card.joker_selected.name))
            else
                card.uianim:Hide()
                card._image2 = card:AddChild(Image(v.atlas, v.image))
                card._image2:ScaleToSize(153, 206)
            end
        end
    end
end

local oldStartPlayGame = BalatroWidget.StartPlayGame
function BalatroWidget:StartPlayGame(...)
    oldStartPlayGame(self, ...)
    if self.root.joker_card and cardlist[self.joker] then
        local v = cardlist[self.joker]
        if v.anim then
            self.root.joker_card:GetAnimState():OverrideSymbol("swap_card1", v.anim,
                v.folername or ("joker_" .. self.joker))
        else
            self.root.joker_card:Hide()
            self.root._image2 = self.root.bg:AddChild(Image(v.atlas, v.image))
            local JOKER_SCALE = 0.7
            self.root._image2:SetScale(JOKER_SCALE * 1)
            self.root._image2:SetPosition(135, 230, 0)
            self.root._image2:SetRotation(12)
            self.root._image2:ScaleToSize(153, 206)
        end
    end
end

--黑塔必定随机到黑塔
AddPrefabPostInit("balatro_machine", function(inst)
    if not TheWorld.ismastersim then
        return inst
    end
    if inst.components.activatable then
        local oldOnActivate = inst.components.activatable.OnActivate
        inst.components.activatable.OnActivate = function(inst, doer, ...)
            if doer and doer:HasTag("qingque") then
                local oldPickSome = GLOBAL.PickSome
                GLOBAL.PickSome = function(num, choices)
                    local ret = oldPickSome(num, choices)
                    if table.contains(choices, "qingque") then
                        if not table.contains(ret, "qingque") then
                            ret[#ret] = "qingque"
                        end
                    end
                    return ret
                end
                oldOnActivate(inst, doer, ...)
                GLOBAL.PickSome = oldPickSome
                return
            end
            return oldOnActivate(inst, doer, ...)
        end
    end
end)
